Further Optimizing a Segmented Sieve of Eratosthenes
Python 3.8 adds math.isqrt().  Using this, you can make a much less cryptic perfect square check:
high == math.isqrt(high) ** 2


Memory Optimization (speed too!)
Using a list of Boolean flags:
numbers = [False]*2 + [True]*(n-2)

is very space inefficient.  You are using 28 bytes per flag!  By packing 8 flags into one byte, you’d achieve a 99.5% memory usage reduction.  Doing this packing yourself would be slow and awkward.  Fortunately, there is a package that does this for you: bitarray.
numbers = bitarray.bitarray(n)
numbers.setall(True)
numbers[0:2] = False

Notice that slice assignment from a scalar Boolean?  It gets better when you use it to cross off the multiples of a prime candidate:
if prime_candidate:
    result.append(index)
    numbers[index*index::index] = False

and done!

Integer division
You should really use integer division // in find_first_multiple_odds():
x = p * ((low // p) + (low % p != 0))
...
return (x - low) // 2


PEP-8

use snake_case function names.  UpperCase is for class names
use a space around binary operators